package com.atguigu.gmall.util.config;

import com.atguigu.gmall.util.service.RabbitService;
import com.atguigu.gmall.util.service.impl.RabbitServiceImpl;
import org.springframework.amqp.rabbit.annotation.EnableRabbit;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.boot.autoconfigure.amqp.RabbitTemplateConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@EnableRabbit
@Configuration
public class MyRabbitConfiguration {

    @Bean
    public RabbitService rabbitService(){
        return new RabbitServiceImpl();
    }


//    @Bean
//    public RabbitService rabbitService(){
//        return new RabbitServiceImpl();
//    }

    /**
     * 微服务之间交互：常用两种方式：
     * 1）、直接RPC进行调用： 现场等待调用结果，接下来进行剩下业务
     *      缺点：每次建立链接浪费性能，网络原因，不靠谱；
     * 2）、发送消息给对方： 消息机制；在微服务中（事件总线）
     *      消息中间件与客户端建立好长连接以后，直接收发数据，及时感知到数据问题，速度快。解耦。
     *      缺点：引入中间件，架构越复杂，稳定性越低
     *
     * 自定义的 RabbitTemplate ；写好两个回调。保证能感知到发送失败的消息
     * @param rabbitConnectionFactory
     * @return
     */
    @Bean
    public RabbitTemplate rabbitTemplate(CachingConnectionFactory rabbitConnectionFactory,
                                         RabbitTemplateConfigurer configurer) {
        RabbitTemplate template = new RabbitTemplate();
        configurer.configure(template,rabbitConnectionFactory);

        //用 template 发出消息以后，消息是否成功发给MQ服务器，并且存储到队列？

        //错误消息：【写一个错误的路由键】 ReturnCallback：状态码：312；状态文本：NO_ROUTE
        //消息给了交换机；交换机存在，如果转不下去触发 ReturnCallback
        template.setReturnCallback((message,
                                    replyCode,
                                    replyText,
                                    exchange,
                                    routingKey) -> {

            System.out.println("ReturnCallback：状态码：" + replyCode + "；状态文本：" + replyText);

            //把所有给队列没投递成功的消息返回;  并保存到数据库表中
            //TODO 数据库中建立一个消息表  t_message(id,content,exchange,routingKey,status,time,user_id,....)；
            //TODO 定时任务扫描数据库中没投递成功的消息，继续投递 rabbittemplate.convertAndSend();



        });

        //确认回调：
        //正常消息：  服务器收到消息，并且存储到队列中。回复ack = true
        //错误消息：
        //1）、交换机错误：ConfirmCallback： ack：false
        //2）、路由键错误： ReturnCallback = 感知到错误，ConfirmCallback = true；
        //3）、队列抵达错误：ReturnCallback = 312，ConfirmCallback = true;
        template.setConfirmCallback((CorrelationData correlationData, boolean ack, String cause) -> {
            //true 代表 交换机收到了消息，  false 交换机都没收到消息
            System.out.println("ConfirmCallback：是否收到：" + ack + "；原因：" + cause);
            if(!ack){
                //没投递成功的
            }else {
                //说明交换机收到消息了
            }

        });


        return template;
    }
}